module core.engine;

import core.events;
import render.renderer;
import scene.scene;

import tango.stdc.stringz;

import derelict.opengl.gl;
import derelict.opengl.glu;
import derelict.sdl.sdl;

import tango.util.container.LinkedList;

alias LinkedList!(EventFeeder) FeedersList;

class Engine {
	private:
		bool _isRunning = false;
		static Engine _instance;
		EventFeeder _eventFeeder;
		Renderer _renderer;
		Scene _scene;
		
	private:
		this() {
			// init graphics
			DerelictGL.load();
			DerelictGLU.load();
			DerelictSDL.load();

			if (SDL_Init(SDL_INIT_VIDEO) < 0)
			{
				throw new Exception("Failed to initialize SDL: " ~ getSDLError());
			}
			
			_eventFeeder = new EventFeeder;
			_renderer = new Renderer;
			_scene = new Scene;
			
			_eventFeeder.addProcessor(_scene);			
		}
		
		~this() {
			SDL_Quit();
		}
		
		void dispatchSDLEvents() {
			SDL_Event event;
			while (SDL_PollEvent(&event))
			{
				switch (event.type)
				{
					case SDL_KEYUP:
						dispatchSDLKeyReleased(event.key.keysym.sym);
						break;
					case SDL_QUIT:
						Engine.instance().stop();
						break;
					default:
						break;
				}
			}
		}
		
		void dispatchSDLKeyReleased(int key)
		{
			switch (key)
			{
				case SDLK_ESCAPE:
					// create shutdown event
					Event e;
					e.type = EventTypes.system;
					e.system.type = SystemEvents.shutdown;

					Engine.instance().eventFeeder().enqueueEvent(e);
					break;
				default:
					break;
			}
		}
	
	public:
		
		bool isRunning() {
			return _isRunning;
		}
		
		void start(char[][] args) {
			
			_isRunning = true;

			// The window title
			const char[] WINDOW_TITLE = "Spaces High";

			_renderer.createGLWindow(WINDOW_TITLE, 640, 480, 16, false);
			_renderer.initGL();
			
			while (1)
			{
				tick(0.01);

				_renderer.drawGLScene(_scene);

				// TODO move to renderer
				SDL_GL_SwapBuffers();
				SDL_Delay(10);
			}

		}
		
		void stop() {
			_isRunning = false;
			Event e;
			e.type = EventTypes.system;
			e.system.type = SystemEvents.shutdown;
			_eventFeeder.enqueueEvent(e);
		}
		
		static Engine instance() {
			if(_instance is null) 
				_instance = new Engine();
			
			return _instance;
		}
		
		EventFeeder eventFeeder() {
			return _eventFeeder;
		}
		
		Renderer renderer() {
			return _renderer;
		}
		
		void tick(double dt) {
			_eventFeeder.flush();
			
			dispatchSDLEvents();
		}
		
		/**
		 * Get the SDL error as a D string.
		 *
		 * Returns: A D string containing the current SDL error.
		 */
		char[] getSDLError()
		{
			return fromStringz(SDL_GetError());
		}
}